package com.r.pan.controller;

import com.r.pan.constant.Constants;
import com.r.pan.entity.dto.ImageCode;
import com.r.pan.entity.dto.LoginFormDTO;
import com.r.pan.entity.dto.RegisterFormDTO;
import com.r.pan.entity.dto.ResetPasswordFormDTO;
import com.r.pan.entity.vo.LoginInfoVO;
import com.r.pan.enums.ImageCodeFor;
import com.r.pan.exception.BadRequestException;
import com.r.pan.result.Result;
import com.r.pan.service.IEmailCodeService;
import com.r.pan.service.IUserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.IOException;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author r-pocky
 * @since 2024-12-16
 */
@Slf4j
@Validated
@RestController
public class AccountController {

    @Resource
    private IEmailCodeService emailCodeService;
    @Resource
    private IUserInfoService userInfoService;

    /**
     * 获取图片验证码
     */
    @GetMapping("/imageCode")
    public void imageCode(HttpServletResponse response,
                          HttpSession session,
                          @NotNull(message = Constants.INVALID__TYPE) Integer type) throws IOException {
        ImageCode imageCode = new ImageCode(130, 38, Constants.LENGTH__CODE, 8);
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        String code = imageCode.getCode();
        log.debug("获取验证码: {}", code);
        ImageCodeFor imageCodeFor = ImageCodeFor.of(type);
        if (ImageCodeFor.EMAIL.equals(imageCodeFor)) {
            // 获取邮箱验证码时发送的获取图片验证码
            session.setAttribute(Constants.SESSION_KEY__IMAGE_CODE_FOR_EMAIL, code);
        } else if (ImageCodeFor.OTHERS.equals(imageCodeFor)) {
            // 其他时候获取的图片验证码
            session.setAttribute(Constants.SESSION_KEY__IMAGE_CODE_FOR_OTHERS, code);
        } else {
            // type参数错误
            throw new BadRequestException(String.format(Constants.EXCEPTION_MESSAGE__PARAMETER_ERROR__FORMAT, "type"));
        }
        imageCode.write(response.getOutputStream());
    }

    /**
     * 向邮箱发送验证码
     */
    @PostMapping("/sendEmailCode")
    public Result<String> sendEmailCode(HttpSession session,
                                        @NotEmpty(message = Constants.INVALID__EMAIL) String email,
                                        @NotEmpty(message = Constants.INVALID__IMAGE_CODE) String imageCode,
                                        @NotNull(message = Constants.INVALID__TYPE) Integer type) {
        log.info("尝试向邮箱 {} 发送验证码...", email);
        validImageCode(session, imageCode, ImageCodeFor.EMAIL);
        // 向邮箱发送验证码
        emailCodeService.sendCodeToEmail(email, type);
        log.info("向邮箱 {} 发送验证码成功", email);
        return Result.success();
    }

    /**
     * 注册账号
     */
    @PostMapping("/register")
    public Result<String> register(HttpSession session, @Valid RegisterFormDTO registerFormDTO) {
        log.info("尝试注册账号 {}", registerFormDTO);
        validImageCode(session, registerFormDTO.getImageCode(), ImageCodeFor.OTHERS);
        emailCodeService.register(registerFormDTO);
        log.info("注册账号成功 {}", registerFormDTO);
        return Result.success();
    }

    /**
     * 登陆账号
     */
    @PostMapping("/login")
    public Result<LoginInfoVO> login(HttpSession session, @Valid LoginFormDTO loginFormDTO) {
        log.info("尝试登录账号 {}", loginFormDTO);
        validImageCode(session, loginFormDTO.getImageCode(), ImageCodeFor.OTHERS);
        LoginInfoVO loginInfoVO = userInfoService.login(loginFormDTO);
        // 保存用户信息到session
        session.setAttribute(Constants.SESSION_KEY__LOGIN_INFO, loginInfoVO);
        log.info("登录账号成功 {}", loginInfoVO);
        return Result.success(loginInfoVO);
    }

    /**
     * 重置密码
     */
    @PostMapping("/resetPassword")
    public Result<String> resetPassword(HttpSession session, @Valid ResetPasswordFormDTO resetPasswordFormDTO) {
        log.info("尝试重置密码 {}", resetPasswordFormDTO);
        validImageCode(session, resetPasswordFormDTO.getImageCode(), ImageCodeFor.OTHERS);
        userInfoService.resetPassword(resetPasswordFormDTO);
        log.info("重置密码成功 {}", resetPasswordFormDTO);
        return Result.success();
    }

    /**
     * 校验传递的图片验证码和session保存的验证码是否相同，通过type来获取保存的不同验证码存储值
     *
     * @param session 请求的session
     * @param code    请求传递的图片验证码
     * @param type    图片验证码作用类型
     */
    private void validImageCode(HttpSession session, String code, ImageCodeFor type) {
        // 根据图片验证码作用类型使用不同的key获取保存的图片验证码信息
        String codeKey;
        if (ImageCodeFor.EMAIL.equals(type)) {
            codeKey = Constants.SESSION_KEY__IMAGE_CODE_FOR_EMAIL;
        } else if (ImageCodeFor.OTHERS.equals(type)) {
            codeKey = Constants.SESSION_KEY__IMAGE_CODE_FOR_OTHERS;
        } else {
            throw new BadRequestException(String.format(Constants.EXCEPTION_MESSAGE__PARAMETER_ERROR__FORMAT, "type"));
        }
        String imageCode = (String) session.getAttribute(codeKey);
        // 业务逻辑成功与否都要移除session保存的图片验证码信息
        session.removeAttribute(codeKey);
        // 判断图片验证码是否正确
        if (imageCode == null || !imageCode.equals(code.toLowerCase())) {
            log.warn("图片验证码错误: {}(given) != {}(session)", code, imageCode);
            throw new BadRequestException("图片验证码错误");
        }
    }
}
